#include "usart.h"
#include <stdarg.h>
// 查看原理图得：
// USART1_Tx - PA9
// USART1_Rx - PA10

#if USART1_USE_IT   // ----------- 中断方式 -------------

// USART1 接收缓冲区
volatile char usart1_recv_buf[USART1_RECV_BUF_SIZE];
volatile int usart1_recv_index;  // 中断接收数据下标
volatile int usart1_get_index;   // 应用读取数据下标
volatile int usart1_idle_flag;   // 空闲标志

/**
 * 简述：初始化 USART1 波特率为 baudrate
 * 参数：baudrate 波特率
 * 返回值：无
 **/
void usart1_init(uint32_t baudrate)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	// 使能 GPIOA 和 USART1 时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
	
	// 配置 PA10 为输入上拉
	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_10;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	// 配置 PA9 为复用功能推挽输出
	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	// 配置 USART1 的波特率、数据位、奇偶校验位、停止位、硬件流控
	USART_InitStruct.USART_BaudRate   = baudrate;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode       = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStruct.USART_Parity     = USART_Parity_No;
	USART_InitStruct.USART_StopBits   = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	// void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
	USART_Init(USART1, &USART_InitStruct);
	
	// NVIC 配置
	NVIC_InitStruct.NVIC_IRQChannel                   = USART1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd                = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 12;  // 抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority        = 0;  // 响应优先级
	// void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
	NVIC_Init(&NVIC_InitStruct);
	
	// 使能 USART1 RXNE 和 IDLE 中断，分别使能，不能按位或
	// void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
	
	// 使能 USART1
	// void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
	USART_Cmd(USART1, ENABLE);
}

/**
 * 简述：USART1 接收一字节数据
 * 参数：无
 * 返回值：返回收到的字节数据
 **/
int usart1_getchar(void)
{
	int data;
	
	// 等待数据
	while (usart1_get_index >= usart1_recv_index);
	
	// 接收数据
	data = usart1_recv_buf[usart1_get_index];
	usart1_get_index++;
	if (usart1_get_index >= usart1_recv_index) {
		usart1_get_index = usart1_recv_index = usart1_idle_flag = 0;
	}
	
	// 回显
	usart1_putchar(data);

	// 返回收到的数据
	return data;
}

#else    // ----------- 轮询方式 -------------

/**
 * 简述：初始化 USART1 波特率为 baudrate
 * 参数：baudrate 波特率
 * 返回值：无
 **/
void usart1_init(uint32_t baudrate)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	
	// 使能 GPIOA 和 USART1 时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
	
	// 配置 PA10 为输入上拉
	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_10;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	// 配置 PA9 为复用功能推挽输出
	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	// 配置 USART1 的波特率、数据位、奇偶校验位、停止位、硬件流控
	USART_InitStruct.USART_BaudRate   = baudrate;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode       = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStruct.USART_Parity     = USART_Parity_No;
	USART_InitStruct.USART_StopBits   = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	// void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
	USART_Init(USART1, &USART_InitStruct);
	
	// 使能 USART1
	// void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
	USART_Cmd(USART1, ENABLE);
}

/**
 * 简述：USART1 接收一字节数据
 * 参数：无
 * 返回值：返回收到的字节数据
 **/
int usart1_getchar(void)
{
	int data;
	
	// 等待数据
	while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
	
	// 接收数据
	// uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
	data = USART_ReceiveData(USART1);
	
	// 回显
	usart1_putchar(data);

	// 返回收到的数据
	return data;
}

#endif  // USART1_USE_IT

/**
 * 简述：USART1 发送一字节数据
 * 参数：ch 要发送的字节数据
 * 返回值：返回发送的字节数据
 **/
int usart1_putchar(int ch)
{
	// 等待可发送数据
	// FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
	// while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
	
	// 发送数据
	// void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
	USART_SendData(USART1, ch);
	
	// 返回发送的数据
	return ch;
}

void usart2_Init(uint32_t baudrate)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//开时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	
	//配置TX
	GPIO_InitStruct.GPIO_Mode      = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin       = GPIO_Pin_2;
	GPIO_InitStruct.GPIO_Speed     = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//配置Rx
	GPIO_InitStruct.GPIO_Mode      = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin	   = GPIO_Pin_3;
	GPIO_InitStruct.GPIO_Speed     = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	
	USART_InitStruct.USART_BaudRate   = baudrate;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode       = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStruct.USART_Parity     = USART_Parity_No;
	USART_InitStruct.USART_StopBits   = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	// void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
	USART_Init(USART2, &USART_InitStruct);
	
	// NVIC 配置
	NVIC_InitStruct.NVIC_IRQChannel                   = USART2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd                = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 12;  // 抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority        = 0;  // 响应优先级
	// void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
	NVIC_Init(&NVIC_InitStruct);
	
	// 使能 USART1 RXNE 和 IDLE 中断，分别使能，不能按位或
	// void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
	
	// 使能 USART1
	// void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
	USART_Cmd(USART2, ENABLE);
}

//串口发送数据
void USART_Send_String(USART_TypeDef* USARTx,char *ch)
{
	while(*ch!='\0'){
		while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
		USART_SendData(USARTx,*ch);
		while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
		ch++;
	}
}

//串口接受数据
uint16_t USART2_Recv(void)
{
	//检查USART2的接收数据寄存器是否已满
	while(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == RESET);
	
	//读取接收到的数据
	return USART_ReceiveData(USART2);
}

static char *itoa( int value, char *string, int radix )
{
    int     i, d;
    int     flag = 0;
    char    *ptr = string;

    /* This implementation only works for decimal numbers. */
    if (radix != 10)
    {
        *ptr = 0;
        return string;
    }

    if (!value)
    {
        *ptr++ = 0x30;
        *ptr = 0;
        return string;
    }

    /* if this is a negative value insert the minus sign. */
    if (value < 0)
    {
        *ptr++ = '-';

        /* Make the value positive. */
        value *= -1;

    }

    for (i = 10000; i > 0; i /= 10)
    {
        d = value / i;

        if (d || flag)
        {
            *ptr++ = (char)(d + 0x30);
            value -= (d * i);
            flag = 1;
        }
    }

    /* Null terminate the string. */
    *ptr = 0;

    return string;

} /* NCL_Itoa */

void USART_printf ( USART_TypeDef * USARTx, char * Data, ... )
{
    const char *s;
    int d;   
    char buf[16];


    va_list ap;
    va_start(ap, Data);

    while ( * Data != 0 )     // 判断数据是否到达结束符
    {                                         
        if ( * Data == 0x5c )  //'\'
        {                                     
            switch ( *++Data )
            {
                case 'r':                                     //回车符
                USART_SendData(USARTx, 0x0d);
                Data ++;
                break;

                case 'n':                                     //换行符
                USART_SendData(USARTx, 0x0a);   
                Data ++;
                break;

                default:
                Data ++;
                break;
            }            
        }

        else if ( * Data == '%')
        {                                     
            switch ( *++Data )
            {               
                case 's':                                         //字符串
                s = va_arg(ap, const char *);
                for ( ; *s; s++) 
                {
                    USART_SendData(USARTx,*s);
                    while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
                }
                Data++;
                break;

                case 'd':           
                    //十进制
                d = va_arg(ap, int);
                itoa(d, buf, 10);
                for (s = buf; *s; s++) 
                {
                    USART_SendData(USARTx,*s);
                    while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
                }
                     Data++;
                     break;
                default:
                     Data++;
                     break;
            }        
        }
        else USART_SendData(USARTx, *Data++);
        while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );

    }
}

// printf 等标准输出函数内部会调用 fputc() 函数，为了将 printf 等的输出转移到 USART1 输出，
// 重新实现 fputc() 函数，在 fputc() 函数中调用 usart1_putchar() 函数，这样就实现了标准输出
// 重定向到 USART1 的输出
// printf() --> fputc() --> usart1_putchar()

// scanf() 等标准输入函数内部会调用 fgetc() 函数，为了 scanf() 等输入转换到从 USART1 输入，
// 重新实现 fgetc() 函数，在 fgect() 函数中调用 usart1_getchar() 函数，就可实现标准输入重定向
// 到 USART1 的输入
// scanf() --> fgetc() --> usart1_getchar()

// 重定向有两种方式：微库，标准库

#ifdef __MICROLIB   // 使用微库，则 Keil 自动增加 __MICROLIB 宏定义

// ----------------------------------- 使用微库重定向 -------------------------------------
int fputc(int ch)  // printf() 等标准输出函数内部会调用 fputc()
{
	return usart1_putchar(ch);
}

int fgetc(void)  // scanf() 等标准输入函数内部会调用 fgetc()
{
	return usart1_getchar();
}

#else

// ---------------------------------- 使用标准库重定向 ------------------------------------

// 复制 Keil 安装目录下的 Retarget.c 文件，进行相应更改。
// C:\Keil_v5\ARM\Startup\Retarget.c

/******************************************************************************/
/* RETARGET.C: 'Retarget' layer for target-dependent low level functions      */
/******************************************************************************/
/* This file is part of the uVision/ARM development tools.                    */
/* Copyright (c) 2005 Keil Software. All rights reserved.                     */
/* This software may only be used under the terms of a valid, current,        */
/* end user licence from KEIL for a compatible version of KEIL software       */
/* development tools. Nothing else gives you the right to use this software.  */
/******************************************************************************/

#include <stdio.h>
#include <time.h>
#include <rt_misc.h>

#pragma import(__use_no_semihosting_swi)

// 屏蔽以下声明
//extern int  sendchar(int ch);  /* in Serial.c */
//extern int  getkey(void);      /* in Serial.c */
//extern long timeval;           /* in Time.c   */


struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;

// 用 usart1_putchar() 替换 sendchar() 函数
int fputc(int ch, FILE *f) {
  //return (sendchar(ch));
	return (usart1_putchar(ch));
}
// 用 usart1_getchar() 替换 sendchar(getkey())
int fgetc(FILE *f) {
  //return (sendchar(getkey()));
	return usart1_getchar();
}


int ferror(FILE *f) {
  /* Your implementation of ferror */
  return EOF;
}

// 用 usart1_putchar() 替换 sendchar() 函数
void _ttywrch(int ch) {
  //sendchar (ch);
	usart1_putchar(ch);
}


void _sys_exit(int return_code) {
  while (1);    /* endless loop */
}

#endif  // USE_MICROLIB

